package com.taocares.sfss.service;

import com.mongodb.client.gridfs.model.GridFSFile;
import com.taocares.sfss.dto.FileDTO;
import com.taocares.sfss.dto.FileResourceDTO;
import com.taocares.sfss.entity.FileEntity;
import com.taocares.sfss.exception.InternalException;
import com.taocares.sfss.exception.SFSSException;
import com.taocares.sfss.repository.GridFsStorage;
import com.taocares.sfss.repository.SimpleFileRepository;
import com.taocares.sfss.service.FileService;
import com.taocares.sfss.util.EntityConverter;
import com.taocares.sfss.util.HashUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.core.io.InputStreamResource;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

@Service
public class FileService
{
    private final SimpleFileRepository simpleFileRepository;
    private final GridFsStorage gridFsStorage;

    @Autowired
    public FileService(SimpleFileRepository simpleFileRepository, GridFsStorage gridFsStorage) {
        this.simpleFileRepository = simpleFileRepository;
        this.gridFsStorage = gridFsStorage;
    }

    public FileDTO store(InputStream stream, String name, String type, long size) {
        String fileId, md5 = "";

        if (stream.markSupported()) {
            try {
                md5 = HashUtil.md5(stream);
                GridFSFile gridFSFile = this.gridFsStorage.findByEtag(md5);
                if (gridFSFile != null) {
                    fileId = gridFSFile.getObjectId().toHexString();
                } else {
                    stream.reset();
                    fileId = this.gridFsStorage.store(stream, name);
                }
            } catch (IOException e) {

                throw new InternalError(e);
            }
        } else {
            fileId = this.gridFsStorage.store(stream, name);
        }
        FileEntity fileEntity = new FileEntity();
        fileEntity.setFileId(fileId);
        fileEntity.setType(type);
        fileEntity.setOriginalName(name);
        fileEntity.setCreatedTime(new Date());
        fileEntity.setSize(size);
        fileEntity.setEtag(md5);
        return EntityConverter.toFileDTO((FileEntity)this.simpleFileRepository.save(fileEntity));
    }

    public FileDTO store(MultipartFile file, String name) {
        String fileId = null, md5;
        try {
            md5 = HashUtil.md5(file.getInputStream());
        } catch (IOException e) {
            throw new InternalError(fileId);
        }

        GridFSFile gridFSFile = this.gridFsStorage.findByEtag(md5);
        if (gridFSFile != null) {
            fileId = gridFSFile.getObjectId().toHexString();
        } else {
            try {
                fileId = this.gridFsStorage.store(file.getInputStream(), name);
            } catch (IOException e) {

                throw new InternalError(e);
            }
        }

        FileEntity fileEntity = new FileEntity();
        fileEntity.setFileId(fileId);
        fileEntity.setType(file.getContentType());
        fileEntity.setOriginalName(name);
        fileEntity.setCreatedTime(new Date());
        fileEntity.setSize(file.getSize());
        fileEntity.setEtag(md5);
        return EntityConverter.toFileDTO((FileEntity)this.simpleFileRepository.save(fileEntity));
    }

    public FileDTO getFileInfo(String id) { return EntityConverter.toFileDTO(findById(id)); }

    public FileResourceDTO getFile(String id) {
        FileEntity file = findById(id);
        GridFsResource gridFsResource = this.gridFsStorage.getFile(file.getFileId());
        return EntityConverter.toFileResourceDTO(file, gridFsResource);
    }

    public FileResourceDTO getFile(String id, Integer width, Integer height, Boolean force) {
        FileEntity file = findById(id);
        GridFsResource gridFsResource = this.gridFsStorage.getFile(file.getFileId());
        try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
            Thumbnails.Builder builder = Thumbnails.of(new InputStream[] { gridFsResource.getInputStream() });
            if (force.booleanValue() && width != null && height != null) {
                builder.forceSize(width.intValue(), height.intValue());
            } else {
                if (width != null) {
                    builder.width(width.intValue());
                }
                if (height != null) {
                    builder.height(height.intValue());
                }
            }
            builder.toOutputStream(stream);
            int length = stream.size();
            InputStreamResource inputStreamResource = new InputStreamResource(new ByteArrayInputStream(stream.toByteArray()));
            FileResourceDTO fileResourceDTO = EntityConverter.toFileResourceDTO(file, inputStreamResource);
            fileResourceDTO.setSize(length);
            return fileResourceDTO;
        } catch (IOException e) {
            e.printStackTrace();
            throw new InternalException("略缩图转换异常");
        }
    }

    @CacheEvict({"fileDTOCache"})
    public void delete(String id) {
        findById(id);
        this.simpleFileRepository.deleteById(id);
    }

    public void delete(List<String> idList) { idList.forEach(this.simpleFileRepository::deleteById); }

    private FileEntity findById(String id) { return (FileEntity)this.simpleFileRepository.findById(id).orElseThrow(() -> new SFSSException(SFSSException.ExceptionType.FILE_NOT_EXIST, "请求文件不存在" + id)); }
}
